package com.zdyt.opc.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.zdyt.opc.common.CustomConnectListener;
import com.zdyt.opc.config.OpcOperation;
import com.zdyt.opc.config.OpcProperties;
import com.zdyt.opc.entity.*;
import com.zdyt.opc.service.OpcCollectService;
import com.zdyt.opc.service.OpcConnectService;
import lombok.extern.slf4j.Slf4j;
import org.jinterop.dcom.common.JIException;
import org.openscada.opc.dcom.list.ClassDetails;
import org.openscada.opc.lib.common.ConnectionInformation;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.*;
import java.net.UnknownHostException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Slf4j
@Service
public class OpcConnectServiceImpl implements OpcConnectService, InitializingBean {

    private static int DEFAULT_WAIT_TIME = 5; //5s

    @Autowired
    OpcOperation opcOperation;

    @Autowired
    OpcCollectService opcCollectService;

    @Autowired
    OpcProperties opcProperties;


    private static AtomicInteger connectInfoId = new AtomicInteger(0);
    private static Map<Integer,ConnectInfo> connectInfoMap = new ConcurrentHashMap<>();

    private Integer connectHostId;
    private String  connectProgId;

    @Override
    public void afterPropertiesSet() throws Exception {
        loadMouldData();
    }


    private void loadMouldData(){
        try {
            File file = new File(opcProperties.getConnectPath());
            FileInputStream fis = new FileInputStream(file);
            InputStreamReader isr = new InputStreamReader(fis);
            BufferedReader br = new BufferedReader(isr);
            String data = null;
            StringBuilder stringBuilder = new StringBuilder();
            while((data = br.readLine()) != null) {
                stringBuilder.append(data);
            }
            String mapStr = stringBuilder.toString();
            connectInfoMap = new ConcurrentHashMap<>();
            if (mapStr.length() > 0){
                connectInfoMap = JSON.parseObject(mapStr,new TypeReference<ConcurrentHashMap<Integer,ConnectInfo>>(){});
                Set<Integer> ids = connectInfoMap.keySet();
                TreeSet<Integer> sortedIds = new TreeSet<>(ids);
                Integer lastId = sortedIds.last();
                connectInfoId = new AtomicInteger(lastId);
            }
            log.info("加载模板文件成功！");
        } catch (Exception e) {
            log.error("加载模板文件失败！");
            connectInfoMap = new ConcurrentHashMap<>();
        }
    }


    @Override
    public ConnectInfo getConnectionInformation() {
        ConnectionInformation connectInfo = opcOperation.getCi();
        ConnectInfo conInfo = new ConnectInfo();
        BeanUtils.copyProperties(connectInfo,conInfo);
        return conInfo;
    }

    @Override
    public List<OPCServerDetail> connectByBaseInfo(ConnectInfo connectInfo) {
        try {
            boolean isCanConnect = opcOperation.tryConnectAndSetInfo(connectInfo.getHost(), connectInfo.getUser(), connectInfo.getPassword(), connectInfo.getDomain());
            if (!isCanConnect){
                connectHostId = null;
                connectProgId = null;
                throw new RuntimeException("连接失败,请检查连接配置是否正确");
            }else {
                connectHostId = connectInfo.getId();
                connectProgId = null;
            }
        }catch (Exception e){
            connectHostId = null;
            connectProgId = null;
            throw e;
        }
        return getAllOPCServerProgIds();
    }

    @Override
    public List<Item> connectToOpcByProgId(String progId)  {
        if (!opcOperation.isServerListConnected()){
            throw new RuntimeException("拒绝连接，请先检查基础连接配置是否正确！");
        }

        List<Item> allItems = new ArrayList<>();
        CountDownLatch countDownLatch = new CountDownLatch(1);
        CustomConnectListener customConnectListener = new CustomConnectListener(countDownLatch);
        opcOperation.connectTargetOpcServer(progId,customConnectListener);
        try {
            // 等待5s, 或者等监听器监听到连接成功主动放行。
            countDownLatch.await(DEFAULT_WAIT_TIME, TimeUnit.SECONDS);
            if (!opcOperation.isConnected()){
                throw new RuntimeException("拒绝连接，请先检查基础连接配置是否正确！");
            }

            // 若连接成功，则获取所有的 Items
            allItems = opcCollectService.getAllItems();

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        connectProgId = progId;
        return allItems;
    }

    @Override
    public void disconnectOpcServer() {
        opcOperation.disConnectServer();
        connectProgId = null;
        connectHostId = null;
    }

    @Override
    public ServerListCanConnect canConnectHost() {
        boolean canConnect = opcOperation.isServerListConnected();
        return new ServerListCanConnect(canConnect);
    }

    @Override
    public OpcServerSate isOpcConnected() {
        boolean connected = opcOperation.isConnected();
        return new OpcServerSate(connected);
    }

    /**
     *  获取所有OPCServer
     */
    @Override
    public List<OPCServerDetail> getAllOPCServerProgIds() {
        if (!opcOperation.isServerListConnected()){
            throw new RuntimeException("拒绝连接，请先检查基础连接配置是否正确！");
        }
        List<OPCServerDetail> serverDetails = new ArrayList<>();
        try {
            List<ClassDetails> allOPCServers = opcOperation.getAllOPCServers();
            serverDetails = allOPCServers.stream()
                    .map((classDetail) ->
                            new OPCServerDetail(classDetail.getClsId(), classDetail.getProgId(), classDetail.getDescription())
                    ).collect(Collectors.toList());

        } catch (JIException | UnknownHostException e) {
            throw new RuntimeException(e.getMessage());
        }
        return serverDetails;
    }

    @Override
    public List<ConnectInfo> createOrUpdateConnect(ConnectInfo connectInfo) {
        Integer id = connectInfo.getId();
        if ( id == null){
            id = connectInfoId.incrementAndGet();
            connectInfo.setId(id);
        }
        connectInfoMap.put(id,connectInfo);

        if (!updateConnectFile()){
            log.error("更新连接信息失败！");
        }

        return new ArrayList<>(connectInfoMap.values());
    }


    public List<ConnectInfo> getConnects(){
        return new ArrayList<>(connectInfoMap.values());
    }

    @Override
    public List<ConnectInfo> deleteConnectInfo(Integer hostId) {
        connectInfoMap.remove(hostId);
        if (!updateConnectFile()){
            log.error("更新连接信息失败！");
        }

        if (hostId.equals(connectHostId) ){
            disconnectOpcServer();
        }
        return new ArrayList<>(connectInfoMap.values());
    }

    @Override
    public NowConnect getNowConnectInfo() {

        NowConnect nowConnect = new NowConnect();
        nowConnect.setProgId(connectProgId);
        nowConnect.setHostId(connectHostId);
        return nowConnect;
    }


    // 持久化模板
    public boolean  updateConnectFile(){
        FileOutputStream fos = null;
        BufferedOutputStream bos = null;
        boolean isWriteToFile  = true;
        try {
            String mouldJSON = JSON.toJSONString(connectInfoMap);

            log.info("序列化字符串:{}",mouldJSON);
            File file = new File(opcProperties.getConnectPath());
            fos = new FileOutputStream(file);
            bos = new BufferedOutputStream(fos);
            bos.write(mouldJSON.getBytes());
            log.info("持久化模板成功！");
            bos.flush();
            return isWriteToFile;
        } catch (Exception e) {
            log.error("持久化模板失败:{}",e.getMessage());
            isWriteToFile = false;
        }finally {
            try {
                if (bos != null){
                    bos.close();
                }
                if (fos != null){
                    fos.close();
                }
            } catch (Exception e) {
                log.error("持久化模板关闭流异常:{}",e.getMessage());
            }

        }
        return isWriteToFile;
    }



}
